package main

import (
	"log"
	"net"
	"os"
	"sync"

	"gitee.com/viplianghl/golib/liangtcp"
	"github.com/jinzhu/configor"
)

var g_tcpServer liangtcp.LiangTCPServer
var g_mapMutex sync.Mutex
var g_mapCS map[net.Conn]net.Conn
var g_mapSC map[net.Conn]net.Conn
var g_listenStr string
var g_connStr string
var g_mon Monitor

// ---------------------------------------------handle client conn

func onConn(conn net.Conn) error {
	// connet to server
	connS, err := net.Dial("tcp", g_connStr)
	if err != nil {
		log.Println("Error, tcp connect failed! ", err)
		return err
	}

	// save to map
	g_mapMutex.Lock()
	g_mapCS[conn] = connS
	g_mapSC[connS] = conn
	g_mapMutex.Unlock()

	g_mon.AddConn(conn)

	// begin handle server
	go handleServerConn(connS)

	return nil
}

func onDisconn(conn net.Conn) error {
	// close server conn
	connS := g_mapCS[conn]
	if connS != nil {
		connS.Close()
	}

	// restore map
	g_mapMutex.Lock()
	delete(g_mapCS, conn)
	if connS != nil {
		delete(g_mapSC, connS)
	}
	g_mapMutex.Unlock()

	g_mon.DelConn(conn)
	return nil
}

func onMsg(conn net.Conn, buf []byte) error {
	// log.Println("reve data (from ", conn.RemoteAddr().String(), "): ", string(buf))

	// send to server
	n, err := g_mapCS[conn].Write(buf)
	if err != nil {
		log.Printf("trans send failed (n=%d)(err=%s)\r\n", n, err.Error())
		return err
	}

	g_mon.AddBytes(conn, n)
	return nil
}

// ---------------------------------------------handle server conn

func handleServerConn(conn net.Conn) {
	defer conn.Close()
	// log.Println("LiangTCP connected: ", conn.RemoteAddr().String())

	for {
		// 接收
		buf := make([]byte, 1600)
		n, err := conn.Read(buf)
		if err != nil {
			log.Printf("LiangTCP read error(from %s): %s\r\n", conn.RemoteAddr().String(), err)
			break
		}
		// log.Printf("LiangTCP read %d bytes(from %s\r\n", n, conn.RemoteAddr().String())

		// 转发给客户端
		g_tcpServer.SendMsg(g_mapSC[conn], buf[:n])
		g_mon.AddBytes(g_mapSC[conn], n)
	}
}

// ---------------------------------------------main

var Config = struct {
	Listenstr string `required:"true"`
	Connstr   string `required:"true"`
	AESKey    string `required:"true"`
}{}

func main() {

	configor.Load(&Config, "./config.yml")
	// check args
	if len(os.Args) < 2 {
		log.Println("use conf.yml Args: ", Config)
		g_listenStr = Config.Listenstr
		g_connStr = Config.Connstr
	} else {
		g_listenStr = os.Args[1]
		g_connStr = os.Args[2]
	}

	// init
	g_mapCS = make(map[net.Conn]net.Conn)
	g_mapSC = make(map[net.Conn]net.Conn)
	g_mon.Init()

	// g_tcpServer := liangtcp.LiangTCPServer{OnMsg: onMsg, OnConn: onConn, OnDisconn: onDisconn}
	g_tcpServer.AESKey = []byte(Config.AESKey)
	g_tcpServer.OnConn = onConn
	g_tcpServer.OnDisconn = onDisconn
	g_tcpServer.OnMsg = onMsg
	g_tcpServer.Listen(g_listenStr)

}
